home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / ClientForm.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  70KB  |  2,436 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. __all__ = [
  5.     'AmbiguityError',
  6.     'CheckboxControl',
  7.     'Control',
  8.     'ControlNotFoundError',
  9.     'FileControl',
  10.     'FormParser',
  11.     'HTMLForm',
  12.     'HiddenControl',
  13.     'IgnoreControl',
  14.     'ImageControl',
  15.     'IsindexControl',
  16.     'Item',
  17.     'ItemCountError',
  18.     'ItemNotFoundError',
  19.     'Label',
  20.     'ListControl',
  21.     'LocateError',
  22.     'Missing',
  23.     'ParseError',
  24.     'ParseFile',
  25.     'ParseFileEx',
  26.     'ParseResponse',
  27.     'ParseResponseEx',
  28.     'PasswordControl',
  29.     'RadioControl',
  30.     'ScalarControl',
  31.     'SelectControl',
  32.     'SubmitButtonControl',
  33.     'SubmitControl',
  34.     'TextControl',
  35.     'TextareaControl',
  36.     'XHTMLCompatibleFormParser']
  37.  
  38. try:
  39.     True
  40. except NameError:
  41.     True = 1
  42.     False = 0
  43.  
  44.  
  45. try:
  46.     bool
  47. except NameError:
  48.     
  49.     def bool(expr):
  50.         if expr:
  51.             return True
  52.         else:
  53.             return False
  54.  
  55.  
  56.  
  57. try:
  58.     import logging
  59.     import inspect
  60. except ImportError:
  61.     
  62.     def debug(msg, *args, **kwds):
  63.         pass
  64.  
  65.  
  66. _logger = logging.getLogger('ClientForm')
  67. OPTIMIZATION_HACK = True
  68.  
  69. def debug(msg, *args, **kwds):
  70.     if OPTIMIZATION_HACK:
  71.         return None
  72.     
  73.     caller_name = inspect.stack()[1][3]
  74.     extended_msg = '%%s %s' % msg
  75.     extended_args = (caller_name,) + args
  76.     debug = _logger.debug(extended_msg, *extended_args, **kwds)
  77.  
  78.  
  79. def _show_debug_messages():
  80.     global OPTIMIZATION_HACK
  81.     OPTIMIZATION_HACK = False
  82.     _logger.setLevel(logging.DEBUG)
  83.     handler = logging.StreamHandler(sys.stdout)
  84.     handler.setLevel(logging.DEBUG)
  85.     _logger.addHandler(handler)
  86.  
  87. import sys
  88. import urllib
  89. import urllib2
  90. import types
  91. import mimetools
  92. import copy
  93. import urlparse
  94. import htmlentitydefs
  95. import re
  96. import random
  97. from cStringIO import StringIO
  98. import sgmllib
  99. sgmllib.charref = re.compile('&#(x?[0-9a-fA-F]+)[^0-9a-fA-F]')
  100.  
  101. try:
  102.     import HTMLParser
  103. except ImportError:
  104.     HAVE_MODULE_HTMLPARSER = False
  105.  
  106. HAVE_MODULE_HTMLPARSER = True
  107.  
  108. try:
  109.     import warnings
  110. except ImportError:
  111.     
  112.     def deprecation(message, stack_offset = 0):
  113.         pass
  114.  
  115.  
  116.  
  117. def deprecation(message, stack_offset = 0):
  118.     warnings.warn(message, DeprecationWarning, stacklevel = 3 + stack_offset)
  119.  
  120. VERSION = '0.2.9'
  121. CHUNK = 1024
  122. DEFAULT_ENCODING = 'latin-1'
  123.  
  124. class Missing:
  125.     pass
  126.  
  127. _compress_re = re.compile('\\s+')
  128.  
  129. def compress_text(text):
  130.     return _compress_re.sub(' ', text.strip())
  131.  
  132.  
  133. def normalize_line_endings(text):
  134.     return re.sub('(?:(?<!\\r)\\n)|(?:\\r(?!\\n))', '\r\n', text)
  135.  
  136.  
  137. def urlencode(query, doseq = False):
  138.     if hasattr(query, 'items'):
  139.         query = query.items()
  140.     else:
  141.         
  142.         try:
  143.             x = len(query)
  144.             if len(query) and type(query[0]) != types.TupleType:
  145.                 raise TypeError()
  146.         except TypeError:
  147.             (ty, va, tb) = sys.exc_info()
  148.             raise TypeError('not a valid non-string sequence or mapping object', tb)
  149.  
  150.     l = []
  151.     if not doseq:
  152.         for k, v in query:
  153.             k = urllib.quote_plus(str(k))
  154.             v = urllib.quote_plus(str(v))
  155.             l.append(k + '=' + v)
  156.         
  157.     else:
  158.         for k, v in query:
  159.             k = urllib.quote_plus(str(k))
  160.             if type(v) == types.StringType:
  161.                 v = urllib.quote_plus(v)
  162.                 l.append(k + '=' + v)
  163.                 continue
  164.             if type(v) == types.UnicodeType:
  165.                 v = urllib.quote_plus(v.encode('ASCII', 'replace'))
  166.                 l.append(k + '=' + v)
  167.                 continue
  168.             
  169.             try:
  170.                 x = len(v)
  171.             except TypeError:
  172.                 v = urllib.quote_plus(str(v))
  173.                 l.append(k + '=' + v)
  174.                 continue
  175.  
  176.             for elt in v:
  177.                 l.append(k + '=' + urllib.quote_plus(str(elt)))
  178.             
  179.         
  180.     return '&'.join(l)
  181.  
  182.  
  183. def unescape(data, entities, encoding = DEFAULT_ENCODING):
  184.     if data is None or '&' not in data:
  185.         return data
  186.     
  187.     
  188.     def replace_entities(match, entities = entities, encoding = encoding):
  189.         ent = match.group()
  190.         if ent[1] == '#':
  191.             return unescape_charref(ent[2:-1], encoding)
  192.         
  193.         repl = entities.get(ent)
  194.         if repl is not None:
  195.             if type(repl) != type(''):
  196.                 
  197.                 try:
  198.                     repl = repl.encode(encoding)
  199.                 except UnicodeError:
  200.                     repl = ent
  201.                 except:
  202.                     None<EXCEPTION MATCH>UnicodeError
  203.                 
  204.  
  205.             None<EXCEPTION MATCH>UnicodeError
  206.         else:
  207.             repl = ent
  208.         return repl
  209.  
  210.     return re.sub('&#?[A-Za-z0-9]+?;', replace_entities, data)
  211.  
  212.  
  213. def unescape_charref(data, encoding):
  214.     name = data
  215.     base = 10
  216.     if name.startswith('x'):
  217.         name = name[1:]
  218.         base = 16
  219.     
  220.     uc = unichr(int(name, base))
  221.     if encoding is None:
  222.         return uc
  223.     else:
  224.         
  225.         try:
  226.             repl = uc.encode(encoding)
  227.         except UnicodeError:
  228.             repl = '&#%s;' % data
  229.  
  230.         return repl
  231.  
  232.  
  233. def get_entitydefs():
  234.     import htmlentitydefs as htmlentitydefs
  235.     latin_1_decode = latin_1_decode
  236.     import codecs
  237.     entitydefs = { }
  238.     
  239.     try:
  240.         htmlentitydefs.name2codepoint
  241.     except AttributeError:
  242.         entitydefs = { }
  243.         for name, char in htmlentitydefs.entitydefs.items():
  244.             uc = latin_1_decode(char)[0]
  245.             if uc.startswith('&#') and uc.endswith(';'):
  246.                 uc = unescape_charref(uc[2:-1], None)
  247.             
  248.             entitydefs['&%s;' % name] = uc
  249.         
  250.  
  251.     for name, codepoint in htmlentitydefs.name2codepoint.items():
  252.         entitydefs['&%s;' % name] = unichr(codepoint)
  253.     
  254.     return entitydefs
  255.  
  256.  
  257. def issequence(x):
  258.     
  259.     try:
  260.         x[0]
  261.     except (TypeError, KeyError):
  262.         return False
  263.     except IndexError:
  264.         pass
  265.  
  266.     return True
  267.  
  268.  
  269. def isstringlike(x):
  270.     
  271.     try:
  272.         x + ''
  273.     except:
  274.         return False
  275.  
  276.     return True
  277.  
  278.  
  279. def choose_boundary():
  280.     nonce = []([ str(random.randint(0, sys.maxint - 1)) for i in (0, 1, 2) ])
  281.     return '-' * 27 + nonce
  282.  
  283.  
  284. class MimeWriter:
  285.     
  286.     def __init__(self, fp, http_hdrs = None):
  287.         self._http_hdrs = http_hdrs
  288.         self._fp = fp
  289.         self._headers = []
  290.         self._boundary = []
  291.         self._first_part = True
  292.  
  293.     
  294.     def addheader(self, key, value, prefix = 0, add_to_http_hdrs = 0):
  295.         lines = value.split('\r\n')
  296.         while lines and not lines[-1]:
  297.             del lines[-1]
  298.         while lines and not lines[0]:
  299.             del lines[0]
  300.         if add_to_http_hdrs:
  301.             value = ''.join(lines)
  302.             self._http_hdrs.append((key, value))
  303.         else:
  304.             for i in range(1, len(lines)):
  305.                 lines[i] = '    ' + lines[i].strip()
  306.             
  307.             value = '\r\n'.join(lines) + '\r\n'
  308.             line = key + ': ' + value
  309.             if prefix:
  310.                 self._headers.insert(0, line)
  311.             else:
  312.                 self._headers.append(line)
  313.  
  314.     
  315.     def flushheaders(self):
  316.         self._fp.writelines(self._headers)
  317.         self._headers = []
  318.  
  319.     
  320.     def startbody(self, ctype = None, plist = [], prefix = 1, add_to_http_hdrs = 0, content_type = 1):
  321.         if content_type and ctype:
  322.             for name, value in plist:
  323.                 ctype = ctype + ';\r\n %s=%s' % (name, value)
  324.             
  325.             self.addheader('Content-type', ctype, prefix = prefix, add_to_http_hdrs = add_to_http_hdrs)
  326.         
  327.         self.flushheaders()
  328.         if not add_to_http_hdrs:
  329.             self._fp.write('\r\n')
  330.         
  331.         self._first_part = True
  332.         return self._fp
  333.  
  334.     
  335.     def startmultipartbody(self, subtype, boundary = None, plist = [], prefix = 1, add_to_http_hdrs = 0, content_type = 1):
  336.         if not boundary:
  337.             pass
  338.         boundary = choose_boundary()
  339.         self._boundary.append(boundary)
  340.         return self.startbody('multipart/' + subtype, [
  341.             ('boundary', boundary)] + plist, prefix = prefix, add_to_http_hdrs = add_to_http_hdrs, content_type = content_type)
  342.  
  343.     
  344.     def nextpart(self):
  345.         boundary = self._boundary[-1]
  346.         if self._first_part:
  347.             self._first_part = False
  348.         else:
  349.             self._fp.write('\r\n')
  350.         self._fp.write('--' + boundary + '\r\n')
  351.         return self.__class__(self._fp)
  352.  
  353.     
  354.     def lastpart(self):
  355.         if self._first_part:
  356.             self.nextpart()
  357.         
  358.         boundary = self._boundary.pop()
  359.         self._fp.write('\r\n--' + boundary + '--\r\n')
  360.  
  361.  
  362.  
  363. class LocateError(ValueError):
  364.     pass
  365.  
  366.  
  367. class AmbiguityError(LocateError):
  368.     pass
  369.  
  370.  
  371. class ControlNotFoundError(LocateError):
  372.     pass
  373.  
  374.  
  375. class ItemNotFoundError(LocateError):
  376.     pass
  377.  
  378.  
  379. class ItemCountError(ValueError):
  380.     pass
  381.  
  382. if HAVE_MODULE_HTMLPARSER:
  383.     SGMLLIB_PARSEERROR = sgmllib.SGMLParseError
  384.     
  385.     class ParseError(sgmllib.SGMLParseError, HTMLParser.HTMLParseError):
  386.         pass
  387.  
  388. elif hasattr(sgmllib, 'SGMLParseError'):
  389.     SGMLLIB_PARSEERROR = sgmllib.SGMLParseError
  390.     
  391.     class ParseError(sgmllib.SGMLParseError):
  392.         pass
  393.  
  394. else:
  395.     SGMLLIB_PARSEERROR = RuntimeError
  396.     
  397.     class ParseError(RuntimeError):
  398.         pass
  399.  
  400.  
  401. class _AbstractFormParser:
  402.     
  403.     def __init__(self, entitydefs = None, encoding = DEFAULT_ENCODING):
  404.         if entitydefs is None:
  405.             entitydefs = get_entitydefs()
  406.         
  407.         self._entitydefs = entitydefs
  408.         self._encoding = encoding
  409.         self.base = None
  410.         self.forms = []
  411.         self.labels = []
  412.         self._current_label = None
  413.         self._current_form = None
  414.         self._select = None
  415.         self._optgroup = None
  416.         self._option = None
  417.         self._textarea = None
  418.         self._global_form = None
  419.         self.start_form([])
  420.         self.end_form()
  421.         self._current_form = self._global_form = self.forms[0]
  422.  
  423.     
  424.     def do_base(self, attrs):
  425.         debug('%s', attrs)
  426.         for key, value in attrs:
  427.             if key == 'href':
  428.                 self.base = self.unescape_attr_if_required(value)
  429.                 continue
  430.         
  431.  
  432.     
  433.     def end_body(self):
  434.         debug('')
  435.         if self._current_label is not None:
  436.             self.end_label()
  437.         
  438.         if self._current_form is not self._global_form:
  439.             self.end_form()
  440.         
  441.  
  442.     
  443.     def start_form(self, attrs):
  444.         debug('%s', attrs)
  445.         if self._current_form is not self._global_form:
  446.             raise ParseError('nested FORMs')
  447.         
  448.         name = None
  449.         action = None
  450.         enctype = 'application/x-www-form-urlencoded'
  451.         method = 'GET'
  452.         d = { }
  453.         for key, value in attrs:
  454.             if key == 'name':
  455.                 name = self.unescape_attr_if_required(value)
  456.             elif key == 'action':
  457.                 action = self.unescape_attr_if_required(value)
  458.             elif key == 'method':
  459.                 method = self.unescape_attr_if_required(value.upper())
  460.             elif key == 'enctype':
  461.                 enctype = self.unescape_attr_if_required(value.lower())
  462.             
  463.             d[key] = self.unescape_attr_if_required(value)
  464.         
  465.         controls = []
  466.         self._current_form = ((name, action, method, enctype), d, controls)
  467.  
  468.     
  469.     def end_form(self):
  470.         debug('')
  471.         if self._current_label is not None:
  472.             self.end_label()
  473.         
  474.         if self._current_form is self._global_form:
  475.             raise ParseError('end of FORM before start')
  476.         
  477.         self.forms.append(self._current_form)
  478.         self._current_form = self._global_form
  479.  
  480.     
  481.     def start_select(self, attrs):
  482.         debug('%s', attrs)
  483.         if self._select is not None:
  484.             raise ParseError('nested SELECTs')
  485.         
  486.         if self._textarea is not None:
  487.             raise ParseError('SELECT inside TEXTAREA')
  488.         
  489.         d = { }
  490.         for key, val in attrs:
  491.             d[key] = self.unescape_attr_if_required(val)
  492.         
  493.         self._select = d
  494.         self._add_label(d)
  495.         self._append_select_control({
  496.             '__select': d })
  497.  
  498.     
  499.     def end_select(self):
  500.         debug('')
  501.         if self._select is None:
  502.             raise ParseError('end of SELECT before start')
  503.         
  504.         if self._option is not None:
  505.             self._end_option()
  506.         
  507.         self._select = None
  508.  
  509.     
  510.     def start_optgroup(self, attrs):
  511.         debug('%s', attrs)
  512.         if self._select is None:
  513.             raise ParseError('OPTGROUP outside of SELECT')
  514.         
  515.         d = { }
  516.         for key, val in attrs:
  517.             d[key] = self.unescape_attr_if_required(val)
  518.         
  519.         self._optgroup = d
  520.  
  521.     
  522.     def end_optgroup(self):
  523.         debug('')
  524.         if self._optgroup is None:
  525.             raise ParseError('end of OPTGROUP before start')
  526.         
  527.         self._optgroup = None
  528.  
  529.     
  530.     def _start_option(self, attrs):
  531.         debug('%s', attrs)
  532.         if self._select is None:
  533.             raise ParseError('OPTION outside of SELECT')
  534.         
  535.         if self._option is not None:
  536.             self._end_option()
  537.         
  538.         d = { }
  539.         for key, val in attrs:
  540.             d[key] = self.unescape_attr_if_required(val)
  541.         
  542.         self._option = { }
  543.         self._option.update(d)
  544.         if self._optgroup and self._optgroup.has_key('disabled') and not self._option.has_key('disabled'):
  545.             self._option['disabled'] = None
  546.         
  547.  
  548.     
  549.     def _end_option(self):
  550.         debug('')
  551.         if self._option is None:
  552.             raise ParseError('end of OPTION before start')
  553.         
  554.         contents = self._option.get('contents', '').strip()
  555.         self._option['contents'] = contents
  556.         if not self._option.has_key('value'):
  557.             self._option['value'] = contents
  558.         
  559.         if not self._option.has_key('label'):
  560.             self._option['label'] = contents
  561.         
  562.         self._option['__select'] = self._select
  563.         self._append_select_control(self._option)
  564.         self._option = None
  565.  
  566.     
  567.     def _append_select_control(self, attrs):
  568.         debug('%s', attrs)
  569.         controls = self._current_form[2]
  570.         name = self._select.get('name')
  571.         controls.append(('select', name, attrs))
  572.  
  573.     
  574.     def start_textarea(self, attrs):
  575.         debug('%s', attrs)
  576.         if self._textarea is not None:
  577.             raise ParseError('nested TEXTAREAs')
  578.         
  579.         if self._select is not None:
  580.             raise ParseError('TEXTAREA inside SELECT')
  581.         
  582.         d = { }
  583.         for key, val in attrs:
  584.             d[key] = self.unescape_attr_if_required(val)
  585.         
  586.         self._add_label(d)
  587.         self._textarea = d
  588.  
  589.     
  590.     def end_textarea(self):
  591.         debug('')
  592.         if self._textarea is None:
  593.             raise ParseError('end of TEXTAREA before start')
  594.         
  595.         controls = self._current_form[2]
  596.         name = self._textarea.get('name')
  597.         controls.append(('textarea', name, self._textarea))
  598.         self._textarea = None
  599.  
  600.     
  601.     def start_label(self, attrs):
  602.         debug('%s', attrs)
  603.         if self._current_label:
  604.             self.end_label()
  605.         
  606.         d = { }
  607.         for key, val in attrs:
  608.             d[key] = self.unescape_attr_if_required(val)
  609.         
  610.         taken = bool(d.get('for'))
  611.         d['__text'] = ''
  612.         d['__taken'] = taken
  613.         if taken:
  614.             self.labels.append(d)
  615.         
  616.         self._current_label = d
  617.  
  618.     
  619.     def end_label(self):
  620.         debug('')
  621.         label = self._current_label
  622.         if label is None:
  623.             return None
  624.         
  625.         self._current_label = None
  626.         del label['__taken']
  627.  
  628.     
  629.     def _add_label(self, d):
  630.         if self._current_label is not None:
  631.             if not self._current_label['__taken']:
  632.                 self._current_label['__taken'] = True
  633.                 d['__label'] = self._current_label
  634.             
  635.         
  636.  
  637.     
  638.     def handle_data(self, data):
  639.         debug('%s', data)
  640.         if data[0:2] == '\r\n':
  641.             data = data[2:]
  642.         
  643.         if data[0:1] in ('\n', '\r'):
  644.             data = data[1:]
  645.         
  646.         if self._option is not None:
  647.             map = self._option
  648.             key = 'contents'
  649.         elif self._textarea is not None:
  650.             map = self._textarea
  651.             key = 'value'
  652.             data = normalize_line_endings(data)
  653.         elif self._current_label is not None:
  654.             map = self._current_label
  655.             key = '__text'
  656.         else:
  657.             return None
  658.         if not map.has_key(key):
  659.             map[key] = data
  660.         else:
  661.             map[key] = map[key] + data
  662.  
  663.     
  664.     def do_button(self, attrs):
  665.         debug('%s', attrs)
  666.         d = { }
  667.         d['type'] = 'submit'
  668.         for key, val in attrs:
  669.             d[key] = self.unescape_attr_if_required(val)
  670.         
  671.         controls = self._current_form[2]
  672.         type = d['type']
  673.         name = d.get('name')
  674.         type = type + 'button'
  675.         self._add_label(d)
  676.         controls.append((type, name, d))
  677.  
  678.     
  679.     def do_input(self, attrs):
  680.         debug('%s', attrs)
  681.         d = { }
  682.         d['type'] = 'text'
  683.         for key, val in attrs:
  684.             d[key] = self.unescape_attr_if_required(val)
  685.         
  686.         controls = self._current_form[2]
  687.         type = d['type']
  688.         name = d.get('name')
  689.         self._add_label(d)
  690.         controls.append((type, name, d))
  691.  
  692.     
  693.     def do_isindex(self, attrs):
  694.         debug('%s', attrs)
  695.         d = { }
  696.         for key, val in attrs:
  697.             d[key] = self.unescape_attr_if_required(val)
  698.         
  699.         controls = self._current_form[2]
  700.         self._add_label(d)
  701.         controls.append(('isindex', None, d))
  702.  
  703.     
  704.     def handle_entityref(self, name):
  705.         self.handle_data(unescape('&%s;' % name, self._entitydefs, self._encoding))
  706.  
  707.     
  708.     def handle_charref(self, name):
  709.         self.handle_data(unescape_charref(name, self._encoding))
  710.  
  711.     
  712.     def unescape_attr(self, name):
  713.         return unescape(name, self._entitydefs, self._encoding)
  714.  
  715.     
  716.     def unescape_attrs(self, attrs):
  717.         escaped_attrs = { }
  718.         for key, val in attrs.items():
  719.             
  720.             try:
  721.                 val.items
  722.             except AttributeError:
  723.                 escaped_attrs[key] = self.unescape_attr(val)
  724.                 continue
  725.  
  726.             escaped_attrs[key] = self.unescape_attrs(val)
  727.         
  728.         return escaped_attrs
  729.  
  730.     
  731.     def unknown_entityref(self, ref):
  732.         self.handle_data('&%s;' % ref)
  733.  
  734.     
  735.     def unknown_charref(self, ref):
  736.         self.handle_data('&#%s;' % ref)
  737.  
  738.  
  739. if not HAVE_MODULE_HTMLPARSER:
  740.     
  741.     class XHTMLCompatibleFormParser:
  742.         
  743.         def __init__(self, entitydefs = None, encoding = DEFAULT_ENCODING):
  744.             raise ValueError('HTMLParser could not be imported')
  745.  
  746.  
  747. else:
  748.     
  749.     class XHTMLCompatibleFormParser(_AbstractFormParser, HTMLParser.HTMLParser):
  750.         
  751.         def __init__(self, entitydefs = None, encoding = DEFAULT_ENCODING):
  752.             HTMLParser.HTMLParser.__init__(self)
  753.             _AbstractFormParser.__init__(self, entitydefs, encoding)
  754.  
  755.         
  756.         def feed(self, data):
  757.             
  758.             try:
  759.                 HTMLParser.HTMLParser.feed(self, data)
  760.             except HTMLParser.HTMLParseError:
  761.                 exc = None
  762.                 raise ParseError(exc)
  763.  
  764.  
  765.         
  766.         def start_option(self, attrs):
  767.             _AbstractFormParser._start_option(self, attrs)
  768.  
  769.         
  770.         def end_option(self):
  771.             _AbstractFormParser._end_option(self)
  772.  
  773.         
  774.         def handle_starttag(self, tag, attrs):
  775.             
  776.             try:
  777.                 method = getattr(self, 'start_' + tag)
  778.             except AttributeError:
  779.                 
  780.                 try:
  781.                     method = getattr(self, 'do_' + tag)
  782.                 except AttributeError:
  783.                     pass
  784.  
  785.                 method(attrs)
  786.  
  787.             method(attrs)
  788.  
  789.         
  790.         def handle_endtag(self, tag):
  791.             
  792.             try:
  793.                 method = getattr(self, 'end_' + tag)
  794.             except AttributeError:
  795.                 pass
  796.  
  797.             method()
  798.  
  799.         
  800.         def unescape(self, name):
  801.             return self.unescape_attr(name)
  802.  
  803.         
  804.         def unescape_attr_if_required(self, name):
  805.             return name
  806.  
  807.         
  808.         def unescape_attrs_if_required(self, attrs):
  809.             return attrs
  810.  
  811.  
  812.  
  813. class _AbstractSgmllibParser(_AbstractFormParser):
  814.     
  815.     def do_option(self, attrs):
  816.         _AbstractFormParser._start_option(self, attrs)
  817.  
  818.     if sys.version_info[:2] >= (2, 5):
  819.         entity_or_charref = re.compile('&(?:([a-zA-Z][-.a-zA-Z0-9]*)|#(x?[0-9a-fA-F]+))(;?)')
  820.         
  821.         def convert_entityref(self, name):
  822.             return unescape('&%s;' % name, self._entitydefs, self._encoding)
  823.  
  824.         
  825.         def convert_charref(self, name):
  826.             return unescape_charref('%s' % name, self._encoding)
  827.  
  828.         
  829.         def unescape_attr_if_required(self, name):
  830.             return name
  831.  
  832.         
  833.         def unescape_attrs_if_required(self, attrs):
  834.             return attrs
  835.  
  836.     else:
  837.         
  838.         def unescape_attr_if_required(self, name):
  839.             return self.unescape_attr(name)
  840.  
  841.         
  842.         def unescape_attrs_if_required(self, attrs):
  843.             return self.unescape_attrs(attrs)
  844.  
  845.  
  846.  
  847. class FormParser(_AbstractSgmllibParser, sgmllib.SGMLParser):
  848.     
  849.     def __init__(self, entitydefs = None, encoding = DEFAULT_ENCODING):
  850.         sgmllib.SGMLParser.__init__(self)
  851.         _AbstractFormParser.__init__(self, entitydefs, encoding)
  852.  
  853.     
  854.     def feed(self, data):
  855.         
  856.         try:
  857.             sgmllib.SGMLParser.feed(self, data)
  858.         except SGMLLIB_PARSEERROR:
  859.             exc = None
  860.             raise ParseError(exc)
  861.  
  862.  
  863.  
  864.  
  865. def _create_bs_classes(bs, icbinbs):
  866.     
  867.     class _AbstractBSFormParser(_AbstractSgmllibParser):
  868.         bs_base_class = None
  869.         
  870.         def __init__(self, entitydefs = None, encoding = DEFAULT_ENCODING):
  871.             _AbstractFormParser.__init__(self, entitydefs, encoding)
  872.             self.bs_base_class.__init__(self)
  873.  
  874.         
  875.         def handle_data(self, data):
  876.             _AbstractFormParser.handle_data(self, data)
  877.             self.bs_base_class.handle_data(self, data)
  878.  
  879.         
  880.         def feed(self, data):
  881.             
  882.             try:
  883.                 self.bs_base_class.feed(self, data)
  884.             except SGMLLIB_PARSEERROR:
  885.                 exc = None
  886.                 raise ParseError(exc)
  887.  
  888.  
  889.  
  890.     
  891.     class RobustFormParser(_AbstractBSFormParser, bs):
  892.         pass
  893.  
  894.     RobustFormParser.bs_base_class = bs
  895.     
  896.     class NestingRobustFormParser(_AbstractBSFormParser, icbinbs):
  897.         pass
  898.  
  899.     NestingRobustFormParser.bs_base_class = icbinbs
  900.     return (RobustFormParser, NestingRobustFormParser)
  901.  
  902.  
  903. try:
  904.     if sys.version_info[:2] < (2, 2):
  905.         raise ImportError
  906.     
  907.     import BeautifulSoup
  908. except ImportError:
  909.     pass
  910.  
  911. (RobustFormParser, NestingRobustFormParser) = _create_bs_classes(BeautifulSoup.BeautifulSoup, BeautifulSoup.ICantBelieveItsBeautifulSoup)
  912. __all__ += [
  913.     'RobustFormParser',
  914.     'NestingRobustFormParser']
  915.  
  916. def ParseResponseEx(response, select_default = False, form_parser_class = FormParser, request_class = urllib2.Request, entitydefs = None, encoding = DEFAULT_ENCODING, _urljoin = urlparse.urljoin, _urlparse = urlparse.urlparse, _urlunparse = urlparse.urlunparse):
  917.     return _ParseFileEx(response, response.geturl(), select_default, False, form_parser_class, request_class, entitydefs, False, encoding, _urljoin = _urljoin, _urlparse = _urlparse, _urlunparse = _urlunparse)
  918.  
  919.  
  920. def ParseFileEx(file, base_uri, select_default = False, form_parser_class = FormParser, request_class = urllib2.Request, entitydefs = None, encoding = DEFAULT_ENCODING, _urljoin = urlparse.urljoin, _urlparse = urlparse.urlparse, _urlunparse = urlparse.urlunparse):
  921.     return _ParseFileEx(file, base_uri, select_default, False, form_parser_class, request_class, entitydefs, False, encoding, _urljoin = _urljoin, _urlparse = _urlparse, _urlunparse = _urlunparse)
  922.  
  923.  
  924. def ParseResponse(response, *args, **kwds):
  925.     return _ParseFileEx(response, response.geturl(), *args, **kwds)[1:]
  926.  
  927.  
  928. def ParseFile(file, base_uri, *args, **kwds):
  929.     return _ParseFileEx(file, base_uri, *args, **kwds)[1:]
  930.  
  931.  
  932. def _ParseFileEx(file, base_uri, select_default = False, ignore_errors = False, form_parser_class = FormParser, request_class = urllib2.Request, entitydefs = None, backwards_compat = False, encoding = DEFAULT_ENCODING, _urljoin = urlparse.urljoin, _urlparse = urlparse.urlparse, _urlunparse = urlparse.urlunparse):
  933.     if backwards_compat:
  934.         deprecation('operating in backwards-compatibility mode', 1)
  935.     
  936.     fp = form_parser_class(entitydefs, encoding)
  937.     while None:
  938.         data = file.read(CHUNK)
  939.         
  940.         try:
  941.             fp.feed(data)
  942.         except ParseError:
  943.             e = None
  944.             e.base_uri = base_uri
  945.             raise 
  946.  
  947.         if len(data) != CHUNK:
  948.             break
  949.             continue
  950.         continue
  951.         if fp.base is not None:
  952.             base_uri = fp.base
  953.         
  954.     labels = []
  955.     id_to_labels = { }
  956.     for l in fp.labels:
  957.         label = Label(l)
  958.         labels.append(label)
  959.         for_id = l['for']
  960.         coll = id_to_labels.get(for_id)
  961.         if coll is None:
  962.             id_to_labels[for_id] = [
  963.                 label]
  964.             continue
  965.         coll.append(label)
  966.     
  967.     forms = []
  968.     for name, action, method, enctype in fp.forms:
  969.         attrs = None
  970.         controls = None
  971.         if action is None:
  972.             action = base_uri
  973.         else:
  974.             action = _urljoin(base_uri, action)
  975.         form = HTMLForm(action, method, enctype, name, attrs, request_class, forms, labels, id_to_labels, backwards_compat)
  976.         form._urlparse = _urlparse
  977.         form._urlunparse = _urlunparse
  978.         for ii in range(len(controls)):
  979.             (type, name, attrs) = controls[ii]
  980.             form.new_control(type, name, attrs, select_default = select_default, index = ii * 10)
  981.         
  982.         forms.append(form)
  983.     
  984.     for form in forms:
  985.         form.fixup()
  986.     
  987.     return forms
  988.  
  989.  
  990. class Label:
  991.     
  992.     def __init__(self, attrs):
  993.         self.id = attrs.get('for')
  994.         self._text = attrs.get('__text').strip()
  995.         self._ctext = compress_text(self._text)
  996.         self.attrs = attrs
  997.         self._backwards_compat = False
  998.  
  999.     
  1000.     def __getattr__(self, name):
  1001.         if name == 'text':
  1002.             if self._backwards_compat:
  1003.                 return self._text
  1004.             else:
  1005.                 return self._ctext
  1006.         
  1007.         return getattr(Label, name)
  1008.  
  1009.     
  1010.     def __setattr__(self, name, value):
  1011.         if name == 'text':
  1012.             raise AttributeError('text attribute is read-only')
  1013.         
  1014.         self.__dict__[name] = value
  1015.  
  1016.     
  1017.     def __str__(self):
  1018.         return '<Label(id=%r, text=%r)>' % (self.id, self.text)
  1019.  
  1020.  
  1021.  
  1022. def _get_label(attrs):
  1023.     text = attrs.get('__label')
  1024.     if text is not None:
  1025.         return Label(text)
  1026.     else:
  1027.         return None
  1028.  
  1029.  
  1030. class Control:
  1031.     
  1032.     def __init__(self, type, name, attrs, index = None):
  1033.         raise NotImplementedError()
  1034.  
  1035.     
  1036.     def add_to_form(self, form):
  1037.         self._form = form
  1038.         form.controls.append(self)
  1039.  
  1040.     
  1041.     def fixup(self):
  1042.         pass
  1043.  
  1044.     
  1045.     def is_of_kind(self, kind):
  1046.         raise NotImplementedError()
  1047.  
  1048.     
  1049.     def clear(self):
  1050.         raise NotImplementedError()
  1051.  
  1052.     
  1053.     def __getattr__(self, name):
  1054.         raise NotImplementedError()
  1055.  
  1056.     
  1057.     def __setattr__(self, name, value):
  1058.         raise NotImplementedError()
  1059.  
  1060.     
  1061.     def pairs(self):
  1062.         return [ (k, v) for i, k, v in self._totally_ordered_pairs() ]
  1063.  
  1064.     
  1065.     def _totally_ordered_pairs(self):
  1066.         raise NotImplementedError()
  1067.  
  1068.     
  1069.     def _write_mime_data(self, mw, name, value):
  1070.         mw2 = mw.nextpart()
  1071.         mw2.addheader('Content-disposition', 'form-data; name="%s"' % name, 1)
  1072.         f = mw2.startbody(prefix = 0)
  1073.         f.write(value)
  1074.  
  1075.     
  1076.     def __str__(self):
  1077.         raise NotImplementedError()
  1078.  
  1079.     
  1080.     def get_labels(self):
  1081.         res = []
  1082.         if self._label:
  1083.             res.append(self._label)
  1084.         
  1085.         if self.id:
  1086.             res.extend(self._form._id_to_labels.get(self.id, ()))
  1087.         
  1088.         return res
  1089.  
  1090.  
  1091.  
  1092. class ScalarControl(Control):
  1093.     
  1094.     def __init__(self, type, name, attrs, index = None):
  1095.         self._index = index
  1096.         self._label = _get_label(attrs)
  1097.         self.__dict__['type'] = type.lower()
  1098.         self.__dict__['name'] = name
  1099.         self._value = attrs.get('value')
  1100.         self.disabled = attrs.has_key('disabled')
  1101.         self.readonly = attrs.has_key('readonly')
  1102.         self.id = attrs.get('id')
  1103.         self.attrs = attrs.copy()
  1104.         self._clicked = False
  1105.         self._urlparse = urlparse.urlparse
  1106.         self._urlunparse = urlparse.urlunparse
  1107.  
  1108.     
  1109.     def __getattr__(self, name):
  1110.         if name == 'value':
  1111.             return self.__dict__['_value']
  1112.         else:
  1113.             raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
  1114.  
  1115.     
  1116.     def __setattr__(self, name, value):
  1117.         if name == 'value':
  1118.             if not isstringlike(value):
  1119.                 raise TypeError('must assign a string')
  1120.             elif self.readonly:
  1121.                 raise AttributeError("control '%s' is readonly" % self.name)
  1122.             elif self.disabled:
  1123.                 raise AttributeError("control '%s' is disabled" % self.name)
  1124.             
  1125.             self.__dict__['_value'] = value
  1126.         elif name in ('name', 'type'):
  1127.             raise AttributeError('%s attribute is readonly' % name)
  1128.         else:
  1129.             self.__dict__[name] = value
  1130.  
  1131.     
  1132.     def _totally_ordered_pairs(self):
  1133.         name = self.name
  1134.         value = self.value
  1135.         if name is None and value is None or self.disabled:
  1136.             return []
  1137.         
  1138.         return [
  1139.             (self._index, name, value)]
  1140.  
  1141.     
  1142.     def clear(self):
  1143.         if self.readonly:
  1144.             raise AttributeError("control '%s' is readonly" % self.name)
  1145.         
  1146.         self.__dict__['_value'] = None
  1147.  
  1148.     
  1149.     def __str__(self):
  1150.         name = self.name
  1151.         value = self.value
  1152.         if name is None:
  1153.             name = '<None>'
  1154.         
  1155.         if value is None:
  1156.             value = '<None>'
  1157.         
  1158.         infos = []
  1159.         if self.disabled:
  1160.             infos.append('disabled')
  1161.         
  1162.         if self.readonly:
  1163.             infos.append('readonly')
  1164.         
  1165.         info = ', '.join(infos)
  1166.         if info:
  1167.             info = ' (%s)' % info
  1168.         
  1169.         return '<%s(%s=%s)%s>' % (self.__class__.__name__, name, value, info)
  1170.  
  1171.  
  1172.  
  1173. class TextControl(ScalarControl):
  1174.     
  1175.     def __init__(self, type, name, attrs, index = None):
  1176.         ScalarControl.__init__(self, type, name, attrs, index)
  1177.         if self.type == 'hidden':
  1178.             self.readonly = True
  1179.         
  1180.         if self._value is None:
  1181.             self._value = ''
  1182.         
  1183.  
  1184.     
  1185.     def is_of_kind(self, kind):
  1186.         return kind == 'text'
  1187.  
  1188.  
  1189.  
  1190. class FileControl(ScalarControl):
  1191.     
  1192.     def __init__(self, type, name, attrs, index = None):
  1193.         ScalarControl.__init__(self, type, name, attrs, index)
  1194.         self._value = None
  1195.         self._upload_data = []
  1196.  
  1197.     
  1198.     def is_of_kind(self, kind):
  1199.         return kind == 'file'
  1200.  
  1201.     
  1202.     def clear(self):
  1203.         if self.readonly:
  1204.             raise AttributeError("control '%s' is readonly" % self.name)
  1205.         
  1206.         self._upload_data = []
  1207.  
  1208.     
  1209.     def __setattr__(self, name, value):
  1210.         if name in ('value', 'name', 'type'):
  1211.             raise AttributeError('%s attribute is readonly' % name)
  1212.         else:
  1213.             self.__dict__[name] = value
  1214.  
  1215.     
  1216.     def add_file(self, file_object, content_type = None, filename = None):
  1217.         if not hasattr(file_object, 'read'):
  1218.             raise TypeError('file-like object must have read method')
  1219.         
  1220.         if content_type is not None and not isstringlike(content_type):
  1221.             raise TypeError('content type must be None or string-like')
  1222.         
  1223.         if filename is not None and not isstringlike(filename):
  1224.             raise TypeError('filename must be None or string-like')
  1225.         
  1226.         if content_type is None:
  1227.             content_type = 'application/octet-stream'
  1228.         
  1229.         self._upload_data.append((file_object, content_type, filename))
  1230.  
  1231.     
  1232.     def _totally_ordered_pairs(self):
  1233.         if self.name is None or self.disabled:
  1234.             return []
  1235.         
  1236.         return [
  1237.             (self._index, self.name, '')]
  1238.  
  1239.     
  1240.     def _write_mime_data(self, mw, _name, _value):
  1241.         if len(self._upload_data) == 1:
  1242.             (file_object, content_type, filename) = self._upload_data[0]
  1243.             mw2 = mw.nextpart()
  1244.             if not filename or '; filename="%s"' % filename:
  1245.                 pass
  1246.             fn_part = ''
  1247.             disp = 'form-data; name="%s"%s' % (self.name, fn_part)
  1248.             mw2.addheader('Content-disposition', disp, prefix = 1)
  1249.             fh = mw2.startbody(content_type, prefix = 0)
  1250.             fh.write(file_object.read())
  1251.         elif len(self._upload_data) != 0:
  1252.             mw2 = mw.nextpart()
  1253.             disp = 'form-data; name="%s"' % self.name
  1254.             mw2.addheader('Content-disposition', disp, prefix = 1)
  1255.             fh = mw2.startmultipartbody('mixed', prefix = 0)
  1256.             for file_object, content_type, filename in self._upload_data:
  1257.                 mw3 = mw2.nextpart()
  1258.                 if not filename or '; filename="%s"' % filename:
  1259.                     pass
  1260.                 fn_part = ''
  1261.                 disp = 'file%s' % fn_part
  1262.                 mw3.addheader('Content-disposition', disp, prefix = 1)
  1263.                 fh2 = mw3.startbody(content_type, prefix = 0)
  1264.                 fh2.write(file_object.read())
  1265.             
  1266.             mw2.lastpart()
  1267.         
  1268.  
  1269.     
  1270.     def __str__(self):
  1271.         name = self.name
  1272.         if name is None:
  1273.             name = '<None>'
  1274.         
  1275.         if not self._upload_data:
  1276.             value = '<No files added>'
  1277.         else:
  1278.             value = []
  1279.             for file, ctype, filename in self._upload_data:
  1280.                 if filename is None:
  1281.                     value.append('<Unnamed file>')
  1282.                     continue
  1283.                 value.append(filename)
  1284.             
  1285.             value = ', '.join(value)
  1286.         info = []
  1287.         if self.disabled:
  1288.             info.append('disabled')
  1289.         
  1290.         if self.readonly:
  1291.             info.append('readonly')
  1292.         
  1293.         info = ', '.join(info)
  1294.         if info:
  1295.             info = ' (%s)' % info
  1296.         
  1297.         return '<%s(%s=%s)%s>' % (self.__class__.__name__, name, value, info)
  1298.  
  1299.  
  1300.  
  1301. class IsindexControl(ScalarControl):
  1302.     
  1303.     def __init__(self, type, name, attrs, index = None):
  1304.         ScalarControl.__init__(self, type, name, attrs, index)
  1305.         if self._value is None:
  1306.             self._value = ''
  1307.         
  1308.  
  1309.     
  1310.     def is_of_kind(self, kind):
  1311.         return kind in ('text', 'clickable')
  1312.  
  1313.     
  1314.     def _totally_ordered_pairs(self):
  1315.         return []
  1316.  
  1317.     
  1318.     def _click(self, form, coord, return_type, request_class = urllib2.Request):
  1319.         parts = self._urlparse(form.action)
  1320.         rest = parts[:-2]
  1321.         (query, frag) = parts[-2:]
  1322.         parts = rest + (urllib.quote_plus(self.value), None)
  1323.         url = self._urlunparse(parts)
  1324.         req_data = (url, None, [])
  1325.         if return_type == 'pairs':
  1326.             return []
  1327.         elif return_type == 'request_data':
  1328.             return req_data
  1329.         else:
  1330.             return request_class(url)
  1331.  
  1332.     
  1333.     def __str__(self):
  1334.         value = self.value
  1335.         if value is None:
  1336.             value = '<None>'
  1337.         
  1338.         infos = []
  1339.         if self.disabled:
  1340.             infos.append('disabled')
  1341.         
  1342.         if self.readonly:
  1343.             infos.append('readonly')
  1344.         
  1345.         info = ', '.join(infos)
  1346.         if info:
  1347.             info = ' (%s)' % info
  1348.         
  1349.         return '<%s(%s)%s>' % (self.__class__.__name__, value, info)
  1350.  
  1351.  
  1352.  
  1353. class IgnoreControl(ScalarControl):
  1354.     
  1355.     def __init__(self, type, name, attrs, index = None):
  1356.         ScalarControl.__init__(self, type, name, attrs, index)
  1357.         self._value = None
  1358.  
  1359.     
  1360.     def is_of_kind(self, kind):
  1361.         return False
  1362.  
  1363.     
  1364.     def __setattr__(self, name, value):
  1365.         if name == 'value':
  1366.             raise AttributeError("control '%s' is ignored, hence read-only" % self.name)
  1367.         elif name in ('name', 'type'):
  1368.             raise AttributeError('%s attribute is readonly' % name)
  1369.         else:
  1370.             self.__dict__[name] = value
  1371.  
  1372.  
  1373.  
  1374. class Item:
  1375.     
  1376.     def __init__(self, control, attrs, index = None):
  1377.         label = _get_label(attrs)
  1378.         if not label or [
  1379.             label]:
  1380.             pass
  1381.         self.__dict__.update({
  1382.             'name': attrs['value'],
  1383.             '_labels': [],
  1384.             'attrs': attrs,
  1385.             '_control': control,
  1386.             'disabled': attrs.has_key('disabled'),
  1387.             '_selected': False,
  1388.             'id': attrs.get('id'),
  1389.             '_index': index })
  1390.         control.items.append(self)
  1391.  
  1392.     
  1393.     def get_labels(self):
  1394.         res = []
  1395.         res.extend(self._labels)
  1396.         if self.id:
  1397.             res.extend(self._control._form._id_to_labels.get(self.id, ()))
  1398.         
  1399.         return res
  1400.  
  1401.     
  1402.     def __getattr__(self, name):
  1403.         if name == 'selected':
  1404.             return self._selected
  1405.         
  1406.         raise AttributeError(name)
  1407.  
  1408.     
  1409.     def __setattr__(self, name, value):
  1410.         if name == 'selected':
  1411.             self._control._set_selected_state(self, value)
  1412.         elif name == 'disabled':
  1413.             self.__dict__['disabled'] = bool(value)
  1414.         else:
  1415.             raise AttributeError(name)
  1416.  
  1417.     
  1418.     def __str__(self):
  1419.         res = self.name
  1420.         if self.selected:
  1421.             res = '*' + res
  1422.         
  1423.         if self.disabled:
  1424.             res = '(%s)' % res
  1425.         
  1426.         return res
  1427.  
  1428.     
  1429.     def __repr__(self):
  1430.         attrs = [
  1431.             ('name', self.name),
  1432.             ('id', self.id)] + self.attrs.items()
  1433.         return ' '.join % ([], []([ '%s=%r' % (k, v) for k, v in attrs ]))
  1434.  
  1435.  
  1436.  
  1437. def disambiguate(items, nr, **kwds):
  1438.     msgs = []
  1439.     for key, value in kwds.items():
  1440.         msgs.append('%s=%r' % (key, value))
  1441.     
  1442.     msg = ' '.join(msgs)
  1443.     if not items:
  1444.         raise ItemNotFoundError(msg)
  1445.     
  1446.     if nr is None:
  1447.         if len(items) > 1:
  1448.             raise AmbiguityError(msg)
  1449.         
  1450.         nr = 0
  1451.     
  1452.     if len(items) <= nr:
  1453.         raise ItemNotFoundError(msg)
  1454.     
  1455.     return items[nr]
  1456.  
  1457.  
  1458. class ListControl(Control):
  1459.     _label = None
  1460.     
  1461.     def __init__(self, type, name, attrs = { }, select_default = False, called_as_base_class = False, index = None):
  1462.         if not called_as_base_class:
  1463.             raise NotImplementedError()
  1464.         
  1465.         self.__dict__['type'] = type.lower()
  1466.         self.__dict__['name'] = name
  1467.         self._value = attrs.get('value')
  1468.         self.disabled = False
  1469.         self.readonly = False
  1470.         self.id = attrs.get('id')
  1471.         self._closed = False
  1472.         self.items = []
  1473.         self._form = None
  1474.         self._select_default = select_default
  1475.         self._clicked = False
  1476.  
  1477.     
  1478.     def clear(self):
  1479.         self.value = []
  1480.  
  1481.     
  1482.     def is_of_kind(self, kind):
  1483.         if kind == 'list':
  1484.             return True
  1485.         elif kind == 'multilist':
  1486.             return bool(self.multiple)
  1487.         elif kind == 'singlelist':
  1488.             return not (self.multiple)
  1489.         else:
  1490.             return False
  1491.  
  1492.     
  1493.     def get_items(self, name = None, label = None, id = None, exclude_disabled = False):
  1494.         if name is not None and not isstringlike(name):
  1495.             raise TypeError('item name must be string-like')
  1496.         
  1497.         if label is not None and not isstringlike(label):
  1498.             raise TypeError('item label must be string-like')
  1499.         
  1500.         if id is not None and not isstringlike(id):
  1501.             raise TypeError('item id must be string-like')
  1502.         
  1503.         items = []
  1504.         compat = self._form.backwards_compat
  1505.         for o in self.items:
  1506.             if exclude_disabled and o.disabled:
  1507.                 continue
  1508.             
  1509.             if name is not None and o.name != name:
  1510.                 continue
  1511.             
  1512.             if label is not None:
  1513.                 for l in o.get_labels():
  1514.                     if (compat or l.text == label or not compat) and l.text.find(label) > -1:
  1515.                         break
  1516.                         continue
  1517.                 
  1518.             
  1519.             if id is not None and o.id != id:
  1520.                 continue
  1521.             
  1522.             items.append(o)
  1523.         
  1524.         return items
  1525.  
  1526.     
  1527.     def get(self, name = None, label = None, id = None, nr = None, exclude_disabled = False):
  1528.         if nr is None and self._form.backwards_compat:
  1529.             nr = 0
  1530.         
  1531.         items = self.get_items(name, label, id, exclude_disabled)
  1532.         return disambiguate(items, nr, name = name, label = label, id = id)
  1533.  
  1534.     
  1535.     def _get(self, name, by_label = False, nr = None, exclude_disabled = False):
  1536.         if by_label:
  1537.             name = None
  1538.             label = name
  1539.         else:
  1540.             name = name
  1541.             label = None
  1542.         return self.get(name, label, nr, exclude_disabled)
  1543.  
  1544.     
  1545.     def toggle(self, name, by_label = False, nr = None):
  1546.         deprecation('item = control.get(...); item.selected = not item.selected')
  1547.         o = self._get(name, by_label, nr)
  1548.         self._set_selected_state(o, not (o.selected))
  1549.  
  1550.     
  1551.     def set(self, selected, name, by_label = False, nr = None):
  1552.         deprecation('control.get(...).selected = <boolean>')
  1553.         self._set_selected_state(self._get(name, by_label, nr), selected)
  1554.  
  1555.     
  1556.     def _set_selected_state(self, item, action):
  1557.         if self.disabled:
  1558.             raise AttributeError("control '%s' is disabled" % self.name)
  1559.         
  1560.         if self.readonly:
  1561.             raise AttributeError("control '%s' is readonly" % self.name)
  1562.         
  1563.         action == bool(action)
  1564.         compat = self._form.backwards_compat
  1565.         if not compat and item.disabled:
  1566.             raise AttributeError('item is disabled')
  1567.         elif compat and item.disabled and action:
  1568.             raise AttributeError('item is disabled')
  1569.         
  1570.         if self.multiple:
  1571.             item.__dict__['_selected'] = action
  1572.         elif not action:
  1573.             item.__dict__['_selected'] = False
  1574.         else:
  1575.             for o in self.items:
  1576.                 o.__dict__['_selected'] = False
  1577.             
  1578.             item.__dict__['_selected'] = True
  1579.  
  1580.     
  1581.     def toggle_single(self, by_label = None):
  1582.         deprecation('control.items[0].selected = not control.items[0].selected')
  1583.         if len(self.items) != 1:
  1584.             raise ItemCountError("'%s' is not a single-item control" % self.name)
  1585.         
  1586.         item = self.items[0]
  1587.         self._set_selected_state(item, not (item.selected))
  1588.  
  1589.     
  1590.     def set_single(self, selected, by_label = None):
  1591.         deprecation('control.items[0].selected = <boolean>')
  1592.         if len(self.items) != 1:
  1593.             raise ItemCountError("'%s' is not a single-item control" % self.name)
  1594.         
  1595.         self._set_selected_state(self.items[0], selected)
  1596.  
  1597.     
  1598.     def get_item_disabled(self, name, by_label = False, nr = None):
  1599.         deprecation('control.get(...).disabled')
  1600.         return self._get(name, by_label, nr).disabled
  1601.  
  1602.     
  1603.     def set_item_disabled(self, disabled, name, by_label = False, nr = None):
  1604.         deprecation('control.get(...).disabled = <boolean>')
  1605.         self._get(name, by_label, nr).disabled = disabled
  1606.  
  1607.     
  1608.     def set_all_items_disabled(self, disabled):
  1609.         for o in self.items:
  1610.             o.disabled = disabled
  1611.         
  1612.  
  1613.     
  1614.     def get_item_attrs(self, name, by_label = False, nr = None):
  1615.         deprecation('control.get(...).attrs')
  1616.         return self._get(name, by_label, nr).attrs
  1617.  
  1618.     
  1619.     def close_control(self):
  1620.         self._closed = True
  1621.  
  1622.     
  1623.     def add_to_form(self, form):
  1624.         self._form = form
  1625.         if self.name is None:
  1626.             Control.add_to_form(self, form)
  1627.         else:
  1628.             for ii in range(len(form.controls) - 1, -1, -1):
  1629.                 control = form.controls[ii]
  1630.                 if control.name == self.name and control.type == self.type:
  1631.                     if control._closed:
  1632.                         Control.add_to_form(self, form)
  1633.                     else:
  1634.                         control.merge_control(self)
  1635.                     break
  1636.                     continue
  1637.             
  1638.  
  1639.     
  1640.     def merge_control(self, control):
  1641.         self.items.extend(control.items)
  1642.  
  1643.     
  1644.     def fixup(self):
  1645.         for o in self.items:
  1646.             o.__dict__['_control'] = self
  1647.         
  1648.  
  1649.     
  1650.     def __getattr__(self, name):
  1651.         pass
  1652.  
  1653.     
  1654.     def __setattr__(self, name, value):
  1655.         if name == 'value':
  1656.             if self.disabled:
  1657.                 raise AttributeError("control '%s' is disabled" % self.name)
  1658.             
  1659.             if self.readonly:
  1660.                 raise AttributeError("control '%s' is readonly" % self.name)
  1661.             
  1662.             self._set_value(value)
  1663.         elif name in ('name', 'type', 'multiple'):
  1664.             raise AttributeError('%s attribute is readonly' % name)
  1665.         else:
  1666.             self.__dict__[name] = value
  1667.  
  1668.     
  1669.     def _set_value(self, value):
  1670.         if value is None or isstringlike(value):
  1671.             raise TypeError('ListControl, must set a sequence')
  1672.         
  1673.         if not value:
  1674.             compat = self._form.backwards_compat
  1675.             for o in self.items:
  1676.                 if not (o.disabled) or compat:
  1677.                     o.selected = False
  1678.                     continue
  1679.             
  1680.         elif self.multiple:
  1681.             self._multiple_set_value(value)
  1682.         elif len(value) > 1:
  1683.             raise ItemCountError('single selection list, must set sequence of length 0 or 1')
  1684.         else:
  1685.             self._single_set_value(value)
  1686.  
  1687.     
  1688.     def _get_items(self, name, target = 1):
  1689.         all_items = self.get_items(name)
  1690.         items = _[1]
  1691.         on = []
  1692.         off = []
  1693.         for o in items:
  1694.             if o.selected:
  1695.                 on.append(o)
  1696.                 continue
  1697.             None if len(items) < target else []
  1698.             off.append(o)
  1699.         
  1700.         return (on, off)
  1701.  
  1702.     
  1703.     def _single_set_value(self, value):
  1704.         (on, off) = self._get_items(value[0])
  1705.         if not on:
  1706.             off[0].selected = True
  1707.         
  1708.  
  1709.     
  1710.     def _multiple_set_value(self, value):
  1711.         compat = self._form.backwards_compat
  1712.         turn_on = []
  1713.         turn_off = _[1]
  1714.         names = { }
  1715.         for nn in value:
  1716.             if nn in names.keys():
  1717.                 names[nn] += 1
  1718.                 continue
  1719.             []
  1720.             names[nn] = 1
  1721.         
  1722.         for name, count in names.items():
  1723.             (on, off) = self._get_items(name, count)
  1724.             for i in range(count):
  1725.                 if on:
  1726.                     item = on[0]
  1727.                     del on[0]
  1728.                     del turn_off[turn_off.index(item)]
  1729.                     continue
  1730.                 []
  1731.                 item = off[0]
  1732.                 del off[0]
  1733.                 turn_on.append(item)
  1734.             
  1735.         
  1736.         for item in turn_off:
  1737.             item.selected = False
  1738.         
  1739.         for item in turn_on:
  1740.             item.selected = True
  1741.         
  1742.  
  1743.     
  1744.     def set_value_by_label(self, value):
  1745.         if isstringlike(value):
  1746.             raise TypeError(value)
  1747.         
  1748.         if not (self.multiple) and len(value) > 1:
  1749.             raise ItemCountError('single selection list, must set sequence of length 0 or 1')
  1750.         
  1751.         items = []
  1752.         for nn in value:
  1753.             found = self.get_items(label = nn)
  1754.             for o in found:
  1755.                 if self._form.backwards_compat or o not in items:
  1756.                     items.append(o)
  1757.                     break
  1758.                     continue
  1759.                 None if len(found) > 1 else []
  1760.             else:
  1761.                 raise ItemNotFoundError(nn)
  1762.         
  1763.         self.value = []
  1764.         for o in items:
  1765.             o.selected = True
  1766.         
  1767.  
  1768.     
  1769.     def get_value_by_label(self):
  1770.         res = []
  1771.         compat = self._form.backwards_compat
  1772.         for o in self.items:
  1773.             if (not (o.disabled) or compat) and o.selected:
  1774.                 for l in o.get_labels():
  1775.                     if l.text:
  1776.                         res.append(l.text)
  1777.                         break
  1778.                         continue
  1779.                 
  1780.         
  1781.         return res
  1782.  
  1783.     
  1784.     def possible_items(self, by_label = False):
  1785.         deprecation('[item.name for item in self.items]')
  1786.         if by_label:
  1787.             res = []
  1788.             for o in self.items:
  1789.                 for l in o.get_labels():
  1790.                     if l.text:
  1791.                         res.append(l.text)
  1792.                         break
  1793.                         continue
  1794.                 
  1795.             
  1796.             return res
  1797.         
  1798.         return [ o.name for o in self.items ]
  1799.  
  1800.     
  1801.     def _totally_ordered_pairs(self):
  1802.         pass
  1803.  
  1804.     
  1805.     def __str__(self):
  1806.         name = self.name
  1807.         if name is None:
  1808.             name = '<None>'
  1809.         
  1810.         display = [ str(o) for o in self.items ]
  1811.         infos = []
  1812.         if self.readonly:
  1813.             infos.append('readonly')
  1814.         
  1815.         info = ', '.join(infos)
  1816.         if info:
  1817.             info = ' (%s)' % info
  1818.         
  1819.         return '<%s(%s=[%s])%s>' % (self.__class__.__name__, name, ', '.join(display), info)
  1820.  
  1821.  
  1822.  
  1823. class RadioControl(ListControl):
  1824.     
  1825.     def __init__(self, type, name, attrs, select_default = False, index = None):
  1826.         attrs.setdefault('value', 'on')
  1827.         ListControl.__init__(self, type, name, attrs, select_default, called_as_base_class = True, index = index)
  1828.         self.__dict__['multiple'] = False
  1829.         o = Item(self, attrs, index)
  1830.         o.__dict__['_selected'] = attrs.has_key('checked')
  1831.  
  1832.     
  1833.     def fixup(self):
  1834.         ListControl.fixup(self)
  1835.         found = _[1]
  1836.  
  1837.     
  1838.     def get_labels(self):
  1839.         return []
  1840.  
  1841.  
  1842.  
  1843. class CheckboxControl(ListControl):
  1844.     
  1845.     def __init__(self, type, name, attrs, select_default = False, index = None):
  1846.         attrs.setdefault('value', 'on')
  1847.         ListControl.__init__(self, type, name, attrs, select_default, called_as_base_class = True, index = index)
  1848.         self.__dict__['multiple'] = True
  1849.         o = Item(self, attrs, index)
  1850.         o.__dict__['_selected'] = attrs.has_key('checked')
  1851.  
  1852.     
  1853.     def get_labels(self):
  1854.         return []
  1855.  
  1856.  
  1857.  
  1858. class SelectControl(ListControl):
  1859.     
  1860.     def __init__(self, type, name, attrs, select_default = False, index = None):
  1861.         self.attrs = attrs['__select'].copy()
  1862.         self.__dict__['_label'] = _get_label(self.attrs)
  1863.         self.__dict__['id'] = self.attrs.get('id')
  1864.         self.__dict__['multiple'] = self.attrs.has_key('multiple')
  1865.         contents = attrs.get('contents')
  1866.         attrs = attrs.copy()
  1867.         del attrs['__select']
  1868.         ListControl.__init__(self, type, name, self.attrs, select_default, called_as_base_class = True, index = index)
  1869.         self.disabled = self.attrs.has_key('disabled')
  1870.         self.readonly = self.attrs.has_key('readonly')
  1871.         if attrs.has_key('value'):
  1872.             o = Item(self, attrs, index)
  1873.             o.__dict__['_selected'] = attrs.has_key('selected')
  1874.             label = attrs.get('label')
  1875.             if label:
  1876.                 o._labels.append(Label({
  1877.                     '__text': label }))
  1878.                 if contents and contents != label:
  1879.                     o._labels.append(Label({
  1880.                         '__text': contents }))
  1881.                 
  1882.             elif contents:
  1883.                 o._labels.append(Label({
  1884.                     '__text': contents }))
  1885.             
  1886.         
  1887.  
  1888.     
  1889.     def fixup(self):
  1890.         ListControl.fixup(self)
  1891.         found = _[1]
  1892.         if not found:
  1893.             if not (self.multiple) or self._select_default:
  1894.                 for o in self.items:
  1895.                     if not o.disabled:
  1896.                         was_disabled = self.disabled
  1897.                         self.disabled = False
  1898.                         
  1899.                         try:
  1900.                             o.selected = True
  1901.                         finally:
  1902.                             o.disabled = was_disabled
  1903.  
  1904.                         break
  1905.                         continue
  1906.                     []
  1907.                 
  1908.             
  1909.         elif not self.multiple:
  1910.             for o in found[:-1]:
  1911.                 o.selected = False
  1912.             
  1913.         
  1914.  
  1915.  
  1916.  
  1917. class SubmitControl(ScalarControl):
  1918.     
  1919.     def __init__(self, type, name, attrs, index = None):
  1920.         ScalarControl.__init__(self, type, name, attrs, index)
  1921.         if self.value is None:
  1922.             self.value = ''
  1923.         
  1924.         self.readonly = True
  1925.  
  1926.     
  1927.     def get_labels(self):
  1928.         res = []
  1929.         if self.value:
  1930.             res.append(Label({
  1931.                 '__text': self.value }))
  1932.         
  1933.         res.extend(ScalarControl.get_labels(self))
  1934.         return res
  1935.  
  1936.     
  1937.     def is_of_kind(self, kind):
  1938.         return kind == 'clickable'
  1939.  
  1940.     
  1941.     def _click(self, form, coord, return_type, request_class = urllib2.Request):
  1942.         self._clicked = coord
  1943.         r = form._switch_click(return_type, request_class)
  1944.         self._clicked = False
  1945.         return r
  1946.  
  1947.     
  1948.     def _totally_ordered_pairs(self):
  1949.         if not self._clicked:
  1950.             return []
  1951.         
  1952.         return ScalarControl._totally_ordered_pairs(self)
  1953.  
  1954.  
  1955.  
  1956. class ImageControl(SubmitControl):
  1957.     
  1958.     def __init__(self, type, name, attrs, index = None):
  1959.         SubmitControl.__init__(self, type, name, attrs, index)
  1960.         self.readonly = False
  1961.  
  1962.     
  1963.     def _totally_ordered_pairs(self):
  1964.         clicked = self._clicked
  1965.         if self.disabled or not clicked:
  1966.             return []
  1967.         
  1968.         name = self.name
  1969.         if name is None:
  1970.             return []
  1971.         
  1972.         pairs = [
  1973.             (self._index, '%s.x' % name, str(clicked[0])),
  1974.             (self._index + 1, '%s.y' % name, str(clicked[1]))]
  1975.         value = self._value
  1976.         if value:
  1977.             pairs.append((self._index + 2, name, value))
  1978.         
  1979.         return pairs
  1980.  
  1981.     get_labels = ScalarControl.get_labels
  1982.  
  1983.  
  1984. class PasswordControl(TextControl):
  1985.     pass
  1986.  
  1987.  
  1988. class HiddenControl(TextControl):
  1989.     pass
  1990.  
  1991.  
  1992. class TextareaControl(TextControl):
  1993.     pass
  1994.  
  1995.  
  1996. class SubmitButtonControl(SubmitControl):
  1997.     pass
  1998.  
  1999.  
  2000. def is_listcontrol(control):
  2001.     return control.is_of_kind('list')
  2002.  
  2003.  
  2004. class HTMLForm:
  2005.     type2class = {
  2006.         'text': TextControl,
  2007.         'password': PasswordControl,
  2008.         'hidden': HiddenControl,
  2009.         'textarea': TextareaControl,
  2010.         'isindex': IsindexControl,
  2011.         'file': FileControl,
  2012.         'button': IgnoreControl,
  2013.         'buttonbutton': IgnoreControl,
  2014.         'reset': IgnoreControl,
  2015.         'resetbutton': IgnoreControl,
  2016.         'submit': SubmitControl,
  2017.         'submitbutton': SubmitButtonControl,
  2018.         'image': ImageControl,
  2019.         'radio': RadioControl,
  2020.         'checkbox': CheckboxControl,
  2021.         'select': SelectControl }
  2022.     
  2023.     def __init__(self, action, method = 'GET', enctype = 'application/x-www-form-urlencoded', name = None, attrs = None, request_class = urllib2.Request, forms = None, labels = None, id_to_labels = None, backwards_compat = True):
  2024.         self.action = action
  2025.         self.method = method
  2026.         self.enctype = enctype
  2027.         self.name = name
  2028.         if attrs is not None:
  2029.             self.attrs = attrs.copy()
  2030.         else:
  2031.             self.attrs = { }
  2032.         self.controls = []
  2033.         self._request_class = request_class
  2034.         self._forms = forms
  2035.         self._labels = labels
  2036.         self._id_to_labels = id_to_labels
  2037.         self.backwards_compat = backwards_compat
  2038.         self._urlunparse = urlparse.urlunparse
  2039.         self._urlparse = urlparse.urlparse
  2040.  
  2041.     
  2042.     def __getattr__(self, name):
  2043.         if name == 'backwards_compat':
  2044.             return self._backwards_compat
  2045.         
  2046.         return getattr(HTMLForm, name)
  2047.  
  2048.     
  2049.     def __setattr__(self, name, value):
  2050.         if name == 'backwards_compat':
  2051.             name = '_backwards_compat'
  2052.             value = bool(value)
  2053.             for cc in self.controls:
  2054.                 
  2055.                 try:
  2056.                     items = cc.items
  2057.                 except AttributeError:
  2058.                     continue
  2059.                     continue
  2060.  
  2061.                 for ii in items:
  2062.                     for ll in ii.get_labels():
  2063.                         ll._backwards_compat = value
  2064.                     
  2065.                 
  2066.             
  2067.         
  2068.         self.__dict__[name] = value
  2069.  
  2070.     
  2071.     def new_control(self, type, name, attrs, ignore_unknown = False, select_default = False, index = None):
  2072.         type = type.lower()
  2073.         klass = self.type2class.get(type)
  2074.         if klass is None:
  2075.             if ignore_unknown:
  2076.                 klass = IgnoreControl
  2077.             else:
  2078.                 klass = TextControl
  2079.         
  2080.         a = attrs.copy()
  2081.         if issubclass(klass, ListControl):
  2082.             control = klass(type, name, a, select_default, index)
  2083.         else:
  2084.             control = klass(type, name, a, index)
  2085.         if type == 'select' and len(attrs) == 1:
  2086.             for ii in range(len(self.controls) - 1, -1, -1):
  2087.                 ctl = self.controls[ii]
  2088.                 if ctl.type == 'select':
  2089.                     ctl.close_control()
  2090.                     break
  2091.                     continue
  2092.             
  2093.         
  2094.         control.add_to_form(self)
  2095.         control._urlparse = self._urlparse
  2096.         control._urlunparse = self._urlunparse
  2097.  
  2098.     
  2099.     def fixup(self):
  2100.         for control in self.controls:
  2101.             control.fixup()
  2102.         
  2103.         self.backwards_compat = self._backwards_compat
  2104.  
  2105.     
  2106.     def __str__(self):
  2107.         if not self.name or self.name + ' ':
  2108.             pass
  2109.         header = '%s%s %s %s' % ('', self.method, self.action, self.enctype)
  2110.         rep = [
  2111.             header]
  2112.         for control in self.controls:
  2113.             rep.append('  %s' % str(control))
  2114.         
  2115.         return '<%s>' % '\n'.join(rep)
  2116.  
  2117.     
  2118.     def __getitem__(self, name):
  2119.         return self.find_control(name).value
  2120.  
  2121.     
  2122.     def __contains__(self, name):
  2123.         return bool(self.find_control(name))
  2124.  
  2125.     
  2126.     def __setitem__(self, name, value):
  2127.         control = self.find_control(name)
  2128.         
  2129.         try:
  2130.             control.value = value
  2131.         except AttributeError:
  2132.             e = None
  2133.             raise ValueError(str(e))
  2134.  
  2135.  
  2136.     
  2137.     def get_value(self, name = None, type = None, kind = None, id = None, nr = None, by_label = False, label = None):
  2138.         if by_label:
  2139.             deprecation('form.get_value_by_label(...)')
  2140.         
  2141.         c = self.find_control(name, type, kind, id, label = label, nr = nr)
  2142.         if by_label:
  2143.             
  2144.             try:
  2145.                 meth = c.get_value_by_label
  2146.             except AttributeError:
  2147.                 raise NotImplementedError("control '%s' does not yet support by_label" % c.name)
  2148.  
  2149.             return meth()
  2150.         else:
  2151.             return c.value
  2152.  
  2153.     
  2154.     def set_value(self, value, name = None, type = None, kind = None, id = None, nr = None, by_label = False, label = None):
  2155.         if by_label:
  2156.             deprecation('form.get_value_by_label(...)')
  2157.         
  2158.         c = self.find_control(name, type, kind, id, label = label, nr = nr)
  2159.         if by_label:
  2160.             
  2161.             try:
  2162.                 meth = c.set_value_by_label
  2163.             except AttributeError:
  2164.                 raise NotImplementedError("control '%s' does not yet support by_label" % c.name)
  2165.  
  2166.             meth(value)
  2167.         else:
  2168.             c.value = value
  2169.  
  2170.     
  2171.     def get_value_by_label(self, name = None, type = None, kind = None, id = None, label = None, nr = None):
  2172.         c = self.find_control(name, type, kind, id, label = label, nr = nr)
  2173.         return c.get_value_by_label()
  2174.  
  2175.     
  2176.     def set_value_by_label(self, value, name = None, type = None, kind = None, id = None, label = None, nr = None):
  2177.         c = self.find_control(name, type, kind, id, label = label, nr = nr)
  2178.         c.set_value_by_label(value)
  2179.  
  2180.     
  2181.     def set_all_readonly(self, readonly):
  2182.         for control in self.controls:
  2183.             control.readonly = bool(readonly)
  2184.         
  2185.  
  2186.     
  2187.     def clear_all(self):
  2188.         for control in self.controls:
  2189.             control.clear()
  2190.         
  2191.  
  2192.     
  2193.     def clear(self, name = None, type = None, kind = None, id = None, nr = None, label = None):
  2194.         c = self.find_control(name, type, kind, id, label = label, nr = nr)
  2195.         c.clear()
  2196.  
  2197.     
  2198.     def possible_items(self, name = None, type = None, kind = None, id = None, nr = None, by_label = False, label = None):
  2199.         c = self._find_list_control(name, type, kind, id, label, nr)
  2200.         return c.possible_items(by_label)
  2201.  
  2202.     
  2203.     def set(self, selected, item_name, name = None, type = None, kind = None, id = None, nr = None, by_label = False, label = None):
  2204.         self._find_list_control(name, type, kind, id, label, nr).set(selected, item_name, by_label)
  2205.  
  2206.     
  2207.     def toggle(self, item_name, name = None, type = None, kind = None, id = None, nr = None, by_label = False, label = None):
  2208.         self._find_list_control(name, type, kind, id, label, nr).toggle(item_name, by_label)
  2209.  
  2210.     
  2211.     def set_single(self, selected, name = None, type = None, kind = None, id = None, nr = None, by_label = None, label = None):
  2212.         self._find_list_control(name, type, kind, id, label, nr).set_single(selected)
  2213.  
  2214.     
  2215.     def toggle_single(self, name = None, type = None, kind = None, id = None, nr = None, by_label = None, label = None):
  2216.         self._find_list_control(name, type, kind, id, label, nr).toggle_single()
  2217.  
  2218.     
  2219.     def add_file(self, file_object, content_type = None, filename = None, name = None, id = None, nr = None, label = None):
  2220.         self.find_control(name, 'file', id = id, label = label, nr = nr).add_file(file_object, content_type, filename)
  2221.  
  2222.     
  2223.     def click(self, name = None, type = None, id = None, nr = 0, coord = (1, 1), request_class = urllib2.Request, label = None):
  2224.         return self._click(name, type, id, label, nr, coord, 'request', self._request_class)
  2225.  
  2226.     
  2227.     def click_request_data(self, name = None, type = None, id = None, nr = 0, coord = (1, 1), request_class = urllib2.Request, label = None):
  2228.         return self._click(name, type, id, label, nr, coord, 'request_data', self._request_class)
  2229.  
  2230.     
  2231.     def click_pairs(self, name = None, type = None, id = None, nr = 0, coord = (1, 1), label = None):
  2232.         return self._click(name, type, id, label, nr, coord, 'pairs', self._request_class)
  2233.  
  2234.     
  2235.     def find_control(self, name = None, type = None, kind = None, id = None, predicate = None, nr = None, label = None):
  2236.         if name is None and type is None and kind is None and id is None and label is None and predicate is None and nr is None:
  2237.             raise ValueError('at least one argument must be supplied to specify control')
  2238.         
  2239.         return self._find_control(name, type, kind, id, label, predicate, nr)
  2240.  
  2241.     
  2242.     def _find_list_control(self, name = None, type = None, kind = None, id = None, label = None, nr = None):
  2243.         if name is None and type is None and kind is None and id is None and label is None and nr is None:
  2244.             raise ValueError('at least one argument must be supplied to specify control')
  2245.         
  2246.         return self._find_control(name, type, kind, id, label, is_listcontrol, nr)
  2247.  
  2248.     
  2249.     def _find_control(self, name, type, kind, id, label, predicate, nr):
  2250.         if name is not None and name is not Missing and not isstringlike(name):
  2251.             raise TypeError('control name must be string-like')
  2252.         
  2253.         if type is not None and not isstringlike(type):
  2254.             raise TypeError('control type must be string-like')
  2255.         
  2256.         if kind is not None and not isstringlike(kind):
  2257.             raise TypeError('control kind must be string-like')
  2258.         
  2259.         if id is not None and not isstringlike(id):
  2260.             raise TypeError('control id must be string-like')
  2261.         
  2262.         if label is not None and not isstringlike(label):
  2263.             raise TypeError('control label must be string-like')
  2264.         
  2265.         if predicate is not None and not callable(predicate):
  2266.             raise TypeError('control predicate must be callable')
  2267.         
  2268.         if nr is not None and nr < 0:
  2269.             raise ValueError('control number must be a positive integer')
  2270.         
  2271.         orig_nr = nr
  2272.         found = None
  2273.         ambiguous = False
  2274.         if nr is None and self.backwards_compat:
  2275.             nr = 0
  2276.         
  2277.         for control in self.controls:
  2278.             if name is not None or name != control.name:
  2279.                 if name is not Missing or control.name is not None:
  2280.                     continue
  2281.                 
  2282.             if type is not None and type != control.type:
  2283.                 continue
  2284.             
  2285.             if kind is not None and not control.is_of_kind(kind):
  2286.                 continue
  2287.             
  2288.             if id is not None and id != control.id:
  2289.                 continue
  2290.             
  2291.             if predicate and not predicate(control):
  2292.                 continue
  2293.             
  2294.             if label:
  2295.                 for l in control.get_labels():
  2296.                     if l.text.find(label) > -1:
  2297.                         break
  2298.                         continue
  2299.                 
  2300.             
  2301.             if nr is not None:
  2302.                 if nr == 0:
  2303.                     return control
  2304.                 
  2305.                 nr -= 1
  2306.                 continue
  2307.             
  2308.             if found:
  2309.                 ambiguous = True
  2310.                 break
  2311.             
  2312.             found = control
  2313.         
  2314.         if found and not ambiguous:
  2315.             return found
  2316.         
  2317.         description = []
  2318.         if name is not None:
  2319.             description.append('name %s' % repr(name))
  2320.         
  2321.         if type is not None:
  2322.             description.append("type '%s'" % type)
  2323.         
  2324.         if kind is not None:
  2325.             description.append("kind '%s'" % kind)
  2326.         
  2327.         if id is not None:
  2328.             description.append("id '%s'" % id)
  2329.         
  2330.         if label is not None:
  2331.             description.append("label '%s'" % label)
  2332.         
  2333.         if predicate is not None:
  2334.             description.append('predicate %s' % predicate)
  2335.         
  2336.         if orig_nr:
  2337.             description.append('nr %d' % orig_nr)
  2338.         
  2339.         description = ', '.join(description)
  2340.         if ambiguous:
  2341.             raise AmbiguityError('more than one control matching ' + description)
  2342.         elif not found:
  2343.             raise ControlNotFoundError('no control matching ' + description)
  2344.         
  2345.  
  2346.     
  2347.     def _click(self, name, type, id, label, nr, coord, return_type, request_class = urllib2.Request):
  2348.         
  2349.         try:
  2350.             control = self._find_control(name, type, 'clickable', id, label, None, nr)
  2351.         except ControlNotFoundError:
  2352.             if name is not None and type is not None and id is not None or nr != 0:
  2353.                 raise 
  2354.             
  2355.             return self._switch_click(return_type, request_class)
  2356.  
  2357.         return control._click(self, coord, return_type, request_class)
  2358.  
  2359.     
  2360.     def _pairs(self):
  2361.         return [ (k, v) for i, k, v, c_i in self._pairs_and_controls() ]
  2362.  
  2363.     
  2364.     def _pairs_and_controls(self):
  2365.         pairs = []
  2366.         for control_index in range(len(self.controls)):
  2367.             control = self.controls[control_index]
  2368.             for ii, key, val in control._totally_ordered_pairs():
  2369.                 pairs.append((ii, key, val, control_index))
  2370.             
  2371.         
  2372.         pairs.sort()
  2373.         return pairs
  2374.  
  2375.     
  2376.     def _request_data(self):
  2377.         method = self.method.upper()
  2378.         parts = self._urlparse(self.action)
  2379.         rest = parts[:-2]
  2380.         (query, frag) = parts[-2:]
  2381.         if method == 'GET':
  2382.             if self.enctype != 'application/x-www-form-urlencoded':
  2383.                 raise ValueError("unknown GET form encoding type '%s'" % self.enctype)
  2384.             
  2385.             parts = rest + (urlencode(self._pairs()), None)
  2386.             uri = self._urlunparse(parts)
  2387.             return (uri, None, [])
  2388.         elif method == 'POST':
  2389.             parts = rest + (query, None)
  2390.             uri = self._urlunparse(parts)
  2391.             if self.enctype == 'application/x-www-form-urlencoded':
  2392.                 return (uri, urlencode(self._pairs()), [
  2393.                     ('Content-type', self.enctype)])
  2394.             elif self.enctype == 'multipart/form-data':
  2395.                 data = StringIO()
  2396.                 http_hdrs = []
  2397.                 mw = MimeWriter(data, http_hdrs)
  2398.                 f = mw.startmultipartbody('form-data', add_to_http_hdrs = True, prefix = 0)
  2399.                 for ii, k, v, control_index in self._pairs_and_controls():
  2400.                     self.controls[control_index]._write_mime_data(mw, k, v)
  2401.                 
  2402.                 mw.lastpart()
  2403.                 return (uri, data.getvalue(), http_hdrs)
  2404.             else:
  2405.                 raise ValueError("unknown POST form encoding type '%s'" % self.enctype)
  2406.         else:
  2407.             raise ValueError("Unknown method '%s'" % method)
  2408.  
  2409.     
  2410.     def _switch_click(self, return_type, request_class = urllib2.Request):
  2411.         if return_type == 'pairs':
  2412.             return self._pairs()
  2413.         elif return_type == 'request_data':
  2414.             return self._request_data()
  2415.         else:
  2416.             req_data = self._request_data()
  2417.             req = request_class(req_data[0], req_data[1])
  2418.             for key, val in req_data[2]:
  2419.                 add_hdr = req.add_header
  2420.                 if key.lower() == 'content-type':
  2421.                     
  2422.                     try:
  2423.                         add_hdr = req.add_unredirected_header
  2424.                     except AttributeError:
  2425.                         pass
  2426.                     except:
  2427.                         None<EXCEPTION MATCH>AttributeError
  2428.                     
  2429.  
  2430.                 None<EXCEPTION MATCH>AttributeError
  2431.                 add_hdr(key, val)
  2432.             
  2433.             return req
  2434.  
  2435.  
  2436.